package com.woniuxy.interceptor;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.woniuxy.annotation.RequirePerms;
import com.woniuxy.entity.Perms;
import com.woniuxy.service.PermsService;
import com.woniuxy.utils.JWTUtil;
import com.woniuxy.utils.ResponseResults;
import com.woniuxy.utils.ResponseState;
import org.springframework.stereotype.Component;
import org.springframework.web.method.HandlerMethod;
import org.springframework.web.servlet.handler.HandlerInterceptorAdapter;
import javax.annotation.Resource;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.lang.reflect.Method;
import java.util.List;
import java.util.concurrent.atomic.AtomicBoolean;

/**
 * 这里的拦截器的作用：拦截所有的请求。需要确定当前访问的请求是否需要指定的权限，如果需要，则验证当前用户是否有该权限。
 * 有，则放行；没有，则直接拦截返回没有权限的结果
 *
 * 设计模式：单例、工厂模式（简单工厂、工厂方法工厂、抽象工厂）、装饰器、适配器（类适配器、对象适配器、缺省适配器）、观察者
 * 这里使用的是对象适配器模式（注：这些设计模式一定要深入学习）
 *
 * preHandle拦截器机制：在进入方法前，判断有没有请求这个方法的权限
 * 这里体现了AOP切面编程的思想
 */
//@Component  // 别忘记，不然无法注入config
public class PermsInterceptor extends HandlerInterceptorAdapter {

    @Resource
    private PermsService permsService;

    /**
     * preHandle方法会在执行controller的方法之前执行
     * 返回true表示放行，如果为false请求终止
     * 拦截器会拦截：静态资源（图片、js文件...）、controller对象、handler（方法）
     *
     * Object handler属于HandlerMethod类，包含了拦截器拦截的请求中所请求的方法信息（方法名、参数个数、当前请求controller）
     * */
    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
        System.out.println("拦截器拦截：" + request.getRequestURI());
        System.out.println("handler反射类的名字："+handler.getClass().getName());

        // TODO 判断当前是否请求的是controller中的方法
        if (handler instanceof HandlerMethod) {     // 判断handler是否是HandlerMethod类的子类或对象
            // 强转
            HandlerMethod handlerMethod = (HandlerMethod) handler;
            // 通过反射，获取handlerMethod的拦截信息
            System.out.println("当前拦截的方法为："+handlerMethod.getMethod().getName());
            System.out.println("当前拦截的方法参数长度为："+handlerMethod.getMethod().getParameters().length);
            System.out.println("当前拦截的方法为："+handlerMethod.getBean().getClass().getName());
            // 反射原理：获取方法对象，拿到请求的方法的对象
            Method method = handlerMethod.getMethod();


            // TODO 判断该方法上是否使用了权限注解  isAnnotationPresent
            if (method.isAnnotationPresent(RequirePerms.class)) {
                // 有注解，表明访问该方法需要一定的权限才行
                // 得到注解中的权限信息
                // 拿到注解
                RequirePerms annotation = method.getAnnotation(RequirePerms.class);
                // 获取注解中的值 value
                String getPerms = annotation.value();
                System.out.println("访问：" + method.getName() + " 需要 " + getPerms + " 权限");


                // TODO 获取当前用户的id，通过用户id查询权限信息，再用当前用户的所有权限信息与当前方法上需要的权限信息进行比较,
                //  有，放行；否则返回false同时返回没有权限的结果
                // 1. 获取当前用户的uid
                /** 注: 这里无需判断uid是否为空，因为请求已经经过了filter，token一定不为空 */
                int getuid = JWTUtil.getuid(request.getHeader("authorization"));
                // 2. 通过当前用户id作为条件查询权限信息，多表联查_RBAC
                List<Perms> permsList = permsService.findByUid(getuid);


                // 3. 循环判断
//                TODO 如果根据用户id查询到的权限包含了从方法上获取到的权限，则有权限
                AtomicBoolean flag = new AtomicBoolean(false);
                permsList.forEach(p -> {
                    // 有权限
                    if (p.getName().equals(getPerms)) {
                        flag.set(true);
                        return;
                    }
                });
                // 没权限
                if (!flag.get()) {
                    ResponseResults<Object> responseResults = new ResponseResults<>()
                            .setCode(500)
                            .setState(ResponseState.NO_PERMS)
                            .setMsg("没有访问权限");
                    // 将对象转换为json  jackson
                    String json = new ObjectMapper().writeValueAsString(responseResults);
                    response.setHeader("Content-Type", "application/json;charset=utf-8");
                    // 数据写到前端
                    response.getWriter().write(json);
                    return false;
                }
            }
        }
        return super.preHandle(request, response, handler);
    }
}
